#pragma once
#include "InetAddr.hpp"
#include "Common.hpp"
#include "Socket.hpp"
#include <memory>
#include <sys/epoll.h>

namespace kzz
{
    const static int size = 256;
    const static int defaultfd = -1;

    class EpollServer
    {
    public:
        EpollServer(int port)
            : _epfd(-1), _listensocket(std::make_unique<TcpSocket>())
        {
            // 先创造listen套接字
            _listensocket->BuildTcpServerSocketMethod(port);
            // 创建epoll模型
            _epfd = epoll_create(size);
            if (_epfd < 0)
            {
                LOG(LogLevel::ERROR) << "epoll_create error";
                exit(EPOLL_ERR);
            }

            // 把listen套接字加入到epoll模型中
            epoll_event event;
            event.data.fd = _listensocket->Fd();
            event.events = EPOLLIN;

            int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensocket->Fd(), &event);
            if (n < 0)
            {
                LOG(LogLevel::ERROR) << "epoll_ctl error";
                exit(EPOLL_ERR);
            }
        }
        void Start()
        {
            while (true)
            {
                int timeout = -1; // 阻塞等待

                int n = epoll_wait(_epfd, _rvs, size, timeout);

                switch (n)
                {
                case -1: // 出错
                    LOG(LogLevel::ERROR) << "epoll errror";
                    break;
                case 0: // 超时
                    LOG(LogLevel::ERROR) << "timeout";
                    break;
                default: // 有可以处理的事务了
                    LOG(LogLevel::DBUGER) << "有事务就绪了......";
                    Dispatcher(n); // 处理就绪事务
                    break;
                }
            }
            // 开始检测谁就绪
        }
        void Dispatcher(int rnum)
        {
            for (int i = 0; i < rnum; i++)
            {
                // 读事件就绪
                //std::cout << i << " " << std::endl;
                if (_rvs[i].events & EPOLLIN)
                {
                    //
                    if (_rvs[i].data.fd == _listensocket->Fd())
                    {
                       // std::cout << _listensocket->Fd() << std::endl;
                        // listensocket套接字去accept
                        Accept();
                    }
                    else
                    {
                        //std::cout << _rvs[i].data.fd << std::endl;
                        Recv(_rvs[i].data.fd);
                    }
                }
                else
                {
                }
            }
        }
        void Accept()
        {
            InetAddr client;
            int client_fd = _listensocket->Accept(client)->Fd();

            epoll_event event;
            event.data.fd = client_fd;
            event.events = EPOLLIN;

            int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, client_fd, &event);
            if (n < 0)
            {
                LOG(LogLevel::ERROR) << "epoll_ctl error";
                exit(EPOLL_ERR);
            }
        }
        void Recv(int socketfd)
        {
            char buffer[1024];
            int n = recv(socketfd, buffer, sizeof(buffer) - 1, 0);
            // buffer[n]=0;
            if (n < 0)
            {
                // 出错
                // 必须先关闭，后关闭的话关的就是-1了
                // 要从epoll模型中移除这个fd
                LOG(LogLevel::DBUGER) << socketfd << "出错";
                // 2.
                epoll_ctl(_epfd, EPOLL_CTL_DEL, socketfd, nullptr);
                // 1.

                close(socketfd);
            }
            else if (n == 0)
            {
                // 对端关闭
                LOG(LogLevel::DBUGER) << socketfd << "对端关闭";
                // 先移除再关闭，只能移除合法fd
                epoll_ctl(_epfd, EPOLL_CTL_DEL, socketfd, nullptr);
                close(socketfd);
            }
            else
            {
                buffer[n] = 0;
                std::cout << "client echo:" << buffer << std::endl;
            }
        }

        ~EpollServer()
        {
            _listensocket->Close();
            if (_epfd > 0)
                close(_epfd);
        }

    private:
        int _epfd;
        std::unique_ptr<Socket> _listensocket;
        struct epoll_event _rvs[size];
    };
}